package com.walt.zhong.sample.views;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.text.DynamicLayout;
import android.text.SpannableString;
import android.text.TextPaint;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Scroller;

import androidx.annotation.Nullable;

import com.walt.zhong.sample.R;

/**
 * description:
 * Author:zhongxj
 * Email:xianjian@nolovr.com
 * Date:2024/3/20
 */
public class RulerSeekBar extends View {
    private static final String TAG = "RulerSeekBar";
    private final int TOUCH_SLOP;
    private final int MIN_FLING_VELOCITY;
    private final int MAX_FLING_VELOCITY;

    private int bgColor;//背景色

    private int gradationColor;//刻度颜色

    private float shortLineWidth; // 短刻度线宽度

    private float longLineWidth; // 长刻度线宽度

    private float shortGradationLen; // 短刻度长度

    private float longGradationLen; // 长刻度长度

    private int textGradationColor;// 刻度字体颜色

    private float textGradationSize;// 刻度字体的大小

    private int indicatorLineColor; // 中间指针线颜色

    private float indicatorLineWidth;// 中间指针线宽度

    private Bitmap mGestureTipBp = null;

    private float minValue = 0;//最小值

    private float maxValue = 100;// 最大值

    private float currentValue = 50; // 当前值

    private float gradationUnit = 0.1f;// 刻度最小单位

    private int numberPerCount = 10; // 需要绘制的数值

    private float gradationGap; // 刻度间距离

    private float gradationNumberGap; // 刻度与文字间的间距

    private int mMinNumber;//最小数值

    private int mMaxNumber; //最大数值

    private int mCurrentNumber;// 当前数值

    /**
     * 最大数值与最小数值间的距离：(mMaxNumber - mMinNumber) / mNumberUnit * gradationGap
     */
    private float mNumberRangeDistance;
    /**
     * 刻度数值最小单位：gradationUnit * 10
     */
    private int mNumberUnit;
    /**
     * 当前数值与最小值的距离：(mCurrentNumber - minValue) / mNumberUnit * gradationGap
     */
    private float mCurrentDistance;
    /**
     * 控件宽度所占有的数值范围：mWidth / gradationGap * mNumberUnit
     */
    private int mWidthRangeNumber;

    /**
     * 短刻度画笔
     */
    private Paint mShortGradationPaint;

    private Paint mIndicatorPaint;

    // 数字画笔
    private TextPaint mNumPaint;

    /**
     * 长刻度画笔
     */
    private Paint mLongGradationPaint;

    /**
     * 文字画笔
     */
    private TextPaint mTextPaint;
    /**
     * 滑动器
     */
    private Scroller mScroller;
    /**
     * 速度跟踪器
     */
    private VelocityTracker mVelocityTracker;
    /**
     * 尺寸
     */
    private int mWidth, mHalfWidth, mHeight;

    private Bitmap indicatorBp = null; // 指示器的图标

    private int mDownX;
    private int mLastX, mLastY;
    private boolean isMoved;
    private OnValueChangedListener mValueChangedListener;
    private float stopShortGradationY;
    private float stopLongGradationY;
    private float startShortGradationY;
    private float startLongGradationY;
    private Paint mGestureAniPaint = null;
    private float mGestureAniTransX = 0f;

    private DynamicLayout dynamicLayout;
    private SpannableString indicatorTipSpannable;
    private ValueAnimator valueAnimator;
    // 是否播放提示动画，默认播放，如果用户点击了seekBar，则停止播放动画，并取消动画
    private boolean isPlayTipAnim = true;

    private SoundPool mSoundPool;
    private int soundId;

    /**
     * 当前值变化监听器
     */
    public interface OnValueChangedListener {
        void onValueChanged(float value);
    }

    public RulerSeekBar(Context context) {
        this(context, null);
    }

    public RulerSeekBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RulerSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
        ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
        TOUCH_SLOP = viewConfiguration.getScaledTouchSlop();

        MIN_FLING_VELOCITY = viewConfiguration.getScaledMinimumFlingVelocity();
        MAX_FLING_VELOCITY = viewConfiguration.getScaledMaximumFlingVelocity();
        convertValueToNumber();
        init(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mWidth = calculateSize(true, widthMeasureSpec);
        mHeight = calculateSize(false, heightMeasureSpec);
        mHalfWidth = mWidth >> 1;
        if (mWidthRangeNumber == 0) {
            mWidthRangeNumber = (int) (mWidth / gradationGap * mNumberUnit);
        }
        Log.d(TAG, "onMeasure: mWidthRangeNumber: " + mWidthRangeNumber + " ,mNumberUnit: " + mNumberUnit);

        setMeasuredDimension(mWidth, mHeight);
    }

    private int calculateSize(boolean isWidth, int measureSpec) {
        final int mode = MeasureSpec.getMode(measureSpec);
        final int size = MeasureSpec.getSize(measureSpec);
        int realSize = size;
        if (mode == MeasureSpec.AT_MOST) {
            if (!isWidth) {
                int defaultSize = dp2px(74);
                realSize = Math.min(realSize, defaultSize);
            }
        }
        Log.d(TAG, "mode: " + mode + " ,size: " + size + " ,realSize: " + realSize);
        return realSize;
    }

    //
    private void init(Context context) {
        // 短刻度画笔
        mShortGradationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mShortGradationPaint.setStrokeWidth(shortLineWidth);
        mShortGradationPaint.setColor(gradationColor);
        mShortGradationPaint.setStrokeWidth(shortLineWidth);
        mShortGradationPaint.setColor(gradationColor);
        mShortGradationPaint.setStrokeCap(Paint.Cap.ROUND);

        // 长刻度画笔
        mLongGradationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLongGradationPaint.setStrokeWidth(longLineWidth);
        mLongGradationPaint.setStrokeCap(Paint.Cap.ROUND);
        mLongGradationPaint.setColor(Color.parseColor("#FF4AA5FD"));

        // 指针画笔
        int[] colors = new int[]{0x011f8d8, 0xff0ef4cb, 0x800cf2c3};
        LinearGradient linearGradient = new LinearGradient(
                0,
                0,
                100,
                100,
                colors,
                null,
                Shader.TileMode.CLAMP
        );

        mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mIndicatorPaint.setColor(indicatorLineColor);
        mIndicatorPaint.setStrokeWidth(indicatorLineWidth);
        mIndicatorPaint.setStrokeCap(Paint.Cap.ROUND);
        mIndicatorPaint.setShader(linearGradient);
        Bitmap originBp = BitmapFactory.decodeResource(getResources(), R.drawable.indicator);
        indicatorBp = Bitmap.createScaledBitmap(originBp, dp2px(222), dp2px(6.85f), true);
        originBp.recycle();

        // 手势图标画笔
        mGestureAniPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        // 文字画笔
        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextSize(textGradationSize);
        mTextPaint.setColor(textGradationColor);

        mScroller = new Scroller(context);

        mNumPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mNumPaint.setTextSize(textGradationSize);
        mNumPaint.setColor(textGradationColor);

        mSoundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
        soundId = mSoundPool.load(getContext(), R.raw.sound, 1);
    }

    // 播放震动音效
    private void startVibration() {
        performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
    }

    private void playSoundEffect() {
        if (soundId != 0) {
            mSoundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
        }
    }


    private void convertValueToNumber() {
        mMinNumber = (int) (minValue * 10);
        mMaxNumber = (int) (maxValue * 10);
        mCurrentNumber = (int) (currentValue * 10);
        mNumberUnit = (int) (gradationUnit * 10);
        mCurrentDistance = (float) (mCurrentNumber - mMinNumber) / mNumberUnit * gradationGap;
        mNumberRangeDistance = (float) (mMaxNumber - mMinNumber) / mNumberUnit * gradationGap;

        if (mWidth != 0) {
            mWidthRangeNumber = (int) (mWidth / gradationGap * mNumberUnit);
        }

        Log.d(TAG, "convertValueToNumber: mMinNumber: " + mMinNumber + " ,mMaxNumber: "
                + mMaxNumber + " ,mCurrentNumber: " + mCurrentNumber + " ,mNumberUnit: " + mNumberUnit
                + " ,mCurrentDistance: " + mCurrentDistance + " ,mNumberRangeDistance: " + mNumberRangeDistance
                + " ,mWidthRangeNumber: " + mWidthRangeNumber);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        final int x = (int) event.getX();
        final int y = (int) event.getY();

        Log.d(TAG, "onTouchEvent: " + action);
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mScroller.forceFinished(true);
                mDownX = x;
                isMoved = false;
                isPlayTipAnim = false;
                if (valueAnimator != null) {
                    valueAnimator.cancel();
                }
                break;

            case MotionEvent.ACTION_MOVE:
                final int dx = x - mLastX;
                //判断是否已经滑动
                if (!isMoved) {
                    final int dy = y - mLastY;

                    // 滑动的触发条件，水平滑动大于垂直滑动，滑动距离大于阈值
                    if (Math.abs(dx) < Math.abs(dy) || Math.abs(x - mDownX) < TOUCH_SLOP) {
                        break;
                    }

                    isMoved = true;
                }

                mCurrentDistance -= dx;
                calculateValue();
                break;
            case MotionEvent.ACTION_UP:
                // 计算速度:使用1000ms 为单位
                mVelocityTracker.computeCurrentVelocity(1000, MAX_FLING_VELOCITY);
                // 获取速度，速度有方向性，水平方向，左滑为负，右滑为正
                int xVelocity = (int) mVelocityTracker.getXVelocity();
                // 达到速度则惯性滑动，否则缓慢滑动到刻度
                if (Math.abs(xVelocity) >= MIN_FLING_VELOCITY) {
                    mScroller.fling((int) mCurrentDistance, 0, -xVelocity, 0,
                            0, (int) mNumberRangeDistance, 0, 0);
                    invalidate();
                } else {
                    scrollToGradation();
                }
                break;
        }

        mLastX = x;
        mLastY = y;
        return true;
    }

    private void scrollToGradation() {
        mCurrentNumber = mMinNumber + Math.round(mCurrentDistance / gradationGap) * mNumberUnit;
        // 算出的值边界设置，如果当前的值小于最小值，则选最小值，如果当前的值大于最大值，则取最大值
        mCurrentNumber = Math.min(Math.max(mCurrentNumber, mMinNumber), mMaxNumber);
        mCurrentDistance = (float) (mCurrentNumber - mMinNumber) / mNumberUnit * gradationGap;
        currentValue = mCurrentNumber / 10f; // 当前的值是放大了10倍处理的，所以回调值的时候需要缩小10倍
        if (mValueChangedListener != null) {
            mValueChangedListener.onValueChanged(currentValue);
        }

        // 播放音效和震动效果
        playSoundEffect();
        startVibration();

        invalidate();
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        Log.d(TAG, "computeScroll========================>");
        if (mScroller.computeScrollOffset()) {
            playSoundEffect();
            startVibration();
            if (mScroller.getCurrX() != mScroller.getFinalX()) {
                mCurrentDistance = mScroller.getCurrX();
                calculateValue();
            } else {
                scrollToGradation();
            }
        }
    }

    /**
     * 根据distance距离，计算数值
     */

    private void calculateValue() {
        // 限定范围在最大值与最小值之间
        mCurrentDistance = Math.min(Math.max(mCurrentDistance, 0), mNumberRangeDistance);
        mCurrentNumber = mMinNumber + (int) (mCurrentDistance / gradationGap) * mNumberUnit;
        // 因为值放大了10倍处理，所以回调值的时候需要缩小10倍
        currentValue = mCurrentNumber / 10f;
        Log.d(TAG, "currentValue: " + currentValue + ",mCurrentDistance: "
                + mCurrentDistance + " ,mCurrentNumber: " + mCurrentNumber);
        if (mValueChangedListener != null) {
            mValueChangedListener.onValueChanged(currentValue);
        }

        invalidate();
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RulerSeekBar);
        bgColor = ta.getColor(R.styleable.RulerSeekBar_bgColor, Color.TRANSPARENT);
        gradationColor = ta.getColor(R.styleable.RulerSeekBar_gradationColor, Color.WHITE);
        shortLineWidth = ta.getDimension(R.styleable.RulerSeekBar_shortLineWidth, dp2px(3));
        shortGradationLen = ta.getDimension(R.styleable.RulerSeekBar_shortGradationLen, dp2px(20));
        longGradationLen = ta.getDimension(R.styleable.RulerSeekBar_longGradationLen, dp2px(34));

        longLineWidth = ta.getDimension(R.styleable.RulerSeekBar_longLineWidth, dp2px(3));
        textGradationColor = ta.getColor(R.styleable.RulerSeekBar_gradationColor, Color.WHITE);
        textGradationSize = ta.getDimension(R.styleable.RulerSeekBar_textGradationSize, sp2px(14));

        indicatorLineColor = ta.getColor(R.styleable.RulerSeekBar_indicatorLineColor, Color.parseColor("#48b975"));
        indicatorLineWidth = ta.getDimension(R.styleable.RulerSeekBar_indicatorLineWidth, dp2px(3f));

        minValue = ta.getFloat(R.styleable.RulerSeekBar_minValue, 0f);
        maxValue = ta.getFloat(R.styleable.RulerSeekBar_maxValue, 100f);
        currentValue = ta.getFloat(R.styleable.RulerSeekBar_currentValue, 50f);
        gradationUnit = ta.getFloat(R.styleable.RulerSeekBar_gradationGap, 0.1f);
        numberPerCount = ta.getInt(R.styleable.RulerSeekBar_numberPerCount, 10);
        gradationGap = ta.getDimension(R.styleable.RulerSeekBar_gradationGap, dp2px(11));
        gradationNumberGap = ta.getDimension(R.styleable.RulerSeekBar_gradationNumberGap, dp2px(8));
        ta.recycle();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        stopLongGradationY = mHeight - dp2px(20) - gradationNumberGap;
        stopShortGradationY = stopLongGradationY - (longGradationLen - shortGradationLen) / 2;
        startShortGradationY = stopShortGradationY - shortGradationLen;
        startLongGradationY = stopLongGradationY - longGradationLen;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 绘制背景
        canvas.drawColor(bgColor);
        // 绘制刻度和数字
        drawGradation(canvas);
        // 绘制指针
        drawIndicator(canvas);

        // 绘制动画的图标
        if (isPlayTipAnim) {
            drawGestureAniIcon(canvas);
        }
    }

    private void drawGestureAniIcon(Canvas canvas) {
        if (mGestureTipBp == null) {
            Bitmap originBp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_gesture_tip);
            mGestureTipBp = Bitmap.createScaledBitmap(originBp, dp2px(46), dp2px(47), true);
            mGestureAniTransX = mHalfWidth - (float) mGestureTipBp.getWidth() / 2 + dp2px(2);
            originBp.recycle();


            valueAnimator = ValueAnimator.ofFloat(
                    mHalfWidth - 11 * gradationGap,
                    mHalfWidth + 7 * gradationGap); // 此处做动画的范围。按照真实情况合理调整。
            valueAnimator.addUpdateListener(animation -> {
                mGestureAniTransX = (float) animation.getAnimatedValue();
                // Log.d(TAG, "zhongxj111: mGestureAniTransX: " + mGestureAniTransX);
                invalidate();
            });
            valueAnimator.setDuration(2000);
            valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
            valueAnimator.start();
        }

        canvas.drawBitmap(mGestureTipBp,
                mGestureAniTransX,
                stopLongGradationY - (float) mGestureTipBp.getHeight() / 2 - dp2px(15),
                mGestureAniPaint
        );
    }

    private void drawGradation(Canvas canvas) {
        int startNum = ((int) mCurrentDistance - mHalfWidth) / (int) gradationGap * mNumberUnit + mMinNumber;
        final int expendUnit = mNumberUnit << 1;
        Log.d(TAG, "expendUnit: " + expendUnit);
        startNum -= expendUnit;
        if (startNum < minValue) {
            startNum = mMinNumber;
        }

        int rightMaxNum = (startNum + expendUnit) + mWidthRangeNumber + expendUnit;

        if (rightMaxNum > mMaxNumber) {
            rightMaxNum = mMaxNumber;
        }

        float distance = mHalfWidth - (mCurrentDistance - (float) (startNum - mMinNumber) / mNumberUnit * gradationGap);
        final int perUnitCount = mNumberUnit * numberPerCount;
        Log.d(TAG, "startNum: " + startNum + " ,rightNum: "
                + rightMaxNum + " ,perUnitCount: " + perUnitCount + " ,distance: " + distance);
        int factor = Math.round((float) (rightMaxNum - startNum) / perUnitCount) + 1;
        while (startNum <= rightMaxNum) {
            int curPosIndex = startNum % perUnitCount;
            // 给控件开始的6个刻度做渐变效果
            if (distance < 6 * gradationGap) {
                Log.d(TAG, "distance==>" + distance + " ,curPosIndex=>" + curPosIndex +
                        " ,perUnitCount: " + perUnitCount + " ,factor: " + factor
                        + " ,6*gradationGap: " + 6 * gradationGap);
                //计算开始部分的透明值
                int startAlpha = Math.abs((int) (distance));
                mLongGradationPaint.setAlpha(startAlpha);
                mShortGradationPaint.setAlpha(startAlpha);
                mNumPaint.setAlpha(startAlpha);
                // 给控件的结尾做渐变效果
            } else if (distance > mWidth - 6 * gradationGap) {
                // 计算结束的透明值
                int endAlpha = Math.abs((int) ((mWidth + gradationGap) - distance));
                // Log.d(TAG, "zhongxj: endAlpha: " + endAlpha);
                mLongGradationPaint.setAlpha(endAlpha);
                mShortGradationPaint.setAlpha(endAlpha);
                mNumPaint.setAlpha(endAlpha);
            } else {
                {
                    mShortGradationPaint.setAlpha(255);
                    mLongGradationPaint.setAlpha(255);
                    mShortGradationPaint.setColor(gradationColor);
                    mLongGradationPaint.setColor(Color.parseColor("#FF4AA5FD"));
                }
            }

            if (curPosIndex == 0) {
                // 长刻度
                mLongGradationPaint.setStrokeWidth(longLineWidth);
                canvas.drawLine(
                        distance,
                        startLongGradationY,
                        distance,
                        stopLongGradationY,
                        mLongGradationPaint
                );

                // 数值
                float fNum = startNum / 10f;
                String text = Float.toString(fNum);
                if (text.endsWith(".0")) {
                    text = text.substring(0, text.length() - 2);
                }
                final float textWidth = mNumPaint.measureText(text);
                canvas.drawText(
                        text,
                        distance - textWidth * .5f,
                        stopLongGradationY
                                + gradationNumberGap
                                + textGradationSize,
                        mNumPaint
                );
            } else {
                mShortGradationPaint.setStrokeWidth(shortLineWidth);
                canvas.drawLine(distance,
                        startShortGradationY,
                        distance,
                        stopShortGradationY,
                        mShortGradationPaint
                );
            }

            startNum += mNumberUnit;
            distance += gradationGap;
        }
    }

    private void drawIndicator(Canvas canvas) {
        drawIndicatorTipText(canvas);
        canvas.drawBitmap(
                indicatorBp,
                mHalfWidth - (float) indicatorBp.getWidth() / 2,
                startLongGradationY - dp2px(20) - indicatorBp.getHeight(),
                mIndicatorPaint);

        canvas.drawLine(
                mHalfWidth,
                startLongGradationY - dp2px(20),
                mHalfWidth,
                stopLongGradationY,
                mIndicatorPaint
        );
    }

    private void drawIndicatorTipText(Canvas canvas) {
        canvas.save();
        if (indicatorTipSpannable == null) {
            indicatorTipSpannable =
                    new SpannableString("这是一个文本，这是一个文本，这是一个文本");
            // 设置不同的样式
            indicatorTipSpannable.setSpan(
                    new ForegroundColorSpan(0xFFFF0000),
                    0,
                    6,
                    SpannableString.SPAN_INCLUSIVE_INCLUSIVE
            );
            indicatorTipSpannable.setSpan(
                    new StyleSpan(Typeface.BOLD),
                    7,
                    13,
                    SpannableString.SPAN_INCLUSIVE_INCLUSIVE
            );

            indicatorTipSpannable.setSpan(
                    new ForegroundColorSpan(0xFF00FF00),
                    7,
                    13,
                    SpannableString.SPAN_INCLUSIVE_INCLUSIVE
            );
        }

        float allTextLen = mTextPaint.measureText(indicatorTipSpannable.toString());
        int bpLeft = (int) ((mWidth - getPaddingLeft() - getPaddingRight()) / 2 - allTextLen / 2);
        float textPosY = startLongGradationY - dp2px(20) - indicatorBp.getHeight() - dp2px(5);
        canvas.translate(bpLeft, textPosY - mTextPaint.getTextSize());
        if (dynamicLayout == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                dynamicLayout = DynamicLayout
                        .Builder
                        .obtain(indicatorTipSpannable, mTextPaint, (int) allTextLen)
                        .build();
            }
        }

        if (dynamicLayout != null) {
            dynamicLayout.draw(canvas);
        }

        canvas.restore();
    }

    public void setOnValueChangedListener(OnValueChangedListener listener) {
        this.mValueChangedListener = listener;
    }

    /**
     * 设置指示器的提示文字
     */
    public void setIndicatorTips(SpannableString tipSpannableString) {
        this.indicatorTipSpannable = tipSpannableString;
        dynamicLayout = null; // 需要置为空，否则后面设置的spannableString会不生效
        invalidate();
    }

    private int dp2px(float dp) {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                dp,
                getResources().getDisplayMetrics()
        );
    }

    private int sp2px(float sp) {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP,
                sp,
                getResources().getDisplayMetrics()
        );
    }
}
